# 导入 logging 模块，用于日志记录
import logging
# 导入 sys 模块，提供对 Python 解释器使用或维护的一些变量的访问
import sys
# 导入 inspect 模块，提供了获取对象信息的函数，如函数名、行号等
import inspect
# 从 logging.handlers 导入 RotatingFileHandler 类，用于将日志写入文件并进行滚动管理
from logging.handlers import RotatingFileHandler


# 定义 logx 类，用于创建日志记录器的单例实例
class logx:
    # 用于存储单例实例
    _instance = None

    # 定义颜色代码字典，用于控制台输出的颜色设置
    COLOR_CODES = {
        "ERROR": "\033[1;91m",  # 红色
        "WARNING": "\033[1;93m",  # 黄色
        "INFO": "\033[1;94m",  # 蓝色
        "DEBUG": "\033[1;95m",  # 紫色
        "SUCCESS": "\033[1;92m",  # 绿色
        "RESET": "\033[0m",  # 重置颜色
    }

    # 定义日志级别映射字典，将自定义级别名称映射到对应的日志级别
    LEVEL_MAP = {
        "success": 21,  # 自定义级别，介于 INFO 和 WARNING 之间
        "info": logging.INFO,
        "warning": logging.WARNING,
        "error": logging.ERROR,
        "debug": logging.DEBUG,
    }

    # 定义日志格式映射字典，将不同的格式名称映射到对应的日志格式字符串
    FORMAT_MAP = {
        "json": '{"time":"%(asctime)s","level":"%(levelname)s","file":"%(filename)s","caller_function":"%(caller_function)s","line":%(caller_line)d,"message":"%(message)s"}',
        "bracket": "[%(levelname)s] %(message)s",
        "detailed": "%(asctime)s | %(levelname)-8s | %(filename)s:%(caller_function)s:%(caller_line)d - %(message)s",
    }

    def __new__(
        cls,
        name="logx",
        level="debug",
        log_file=None,
        log_format="detailed",
        colorize_console=True,
    ):
        """
        单例模式的实现，确保类只有一个实例。
        如果实例不存在，则创建一个新实例并初始化；如果实例已存在，则直接返回该实例。

        :param name: 日志记录器的名称，默认为 "logx"
        :param level: 日志级别，默认为 "debug"
        :param log_file: 日志文件的路径，默认为 None
        :param log_format: 日志格式，默认为 "detailed"
        :param colorize_console: 是否在控制台输出中使用颜色，默认为 True
        :return: 单例实例
        """
        if not cls._instance:  # 如果实例不存在
            cls._instance = super(logx, cls).__new__(cls)
            # 调用实例的 _initialize 方法进行初始化
            cls._instance._initialize(
                name, level, log_file, log_format, colorize_console
            )
        return cls._instance  # 返回单例实例

    def _initialize(self, name, level, log_file, log_format, colorize_console):
        """
        初始化日志记录器，设置日志级别、添加自定义级别、处理日志格式和添加处理程序。

        :param name: 日志记录器的名称
        :param level: 日志级别
        :param log_file: 日志文件的路径
        :param log_format: 日志格式
        :param colorize_console: 是否在控制台输出中使用颜色
        """
        # 获取指定名称的日志记录器
        self.logger = logging.getLogger(name)
        # 设置日志记录器的级别
        self.logger.setLevel(self.LEVEL_MAP.get(level.lower(), logging.DEBUG))
        # 添加自定义日志级别 "SUCCESS"
        logging.addLevelName(21, "SUCCESS")

        # 保存日志格式
        self.log_format = log_format
        if log_format not in self.FORMAT_MAP:
            # 如果用户提供的格式不在预设格式中，则将其作为自定义格式添加
            self.FORMAT_MAP["custom"] = log_format

        # 保存是否在控制台输出中使用颜色的标志
        self.colorize_console = colorize_console

        if log_file:
            # 如果指定了日志文件，则添加文件处理程序
            self.add_file_handler(log_file)
        # 添加控制台处理程序
        self.add_console_handler()

    def add_console_handler(self):
        """
        添加控制台处理程序，用于将日志输出到控制台。
        根据日志格式和是否使用颜色选择合适的格式化器。
        """
        # 创建一个流处理程序，将日志输出到标准输出
        console_handler = logging.StreamHandler(sys.stdout)
        # 设置处理程序的日志级别
        console_handler.setLevel(self.logger.level)
        if self.log_format == "json":
            # 如果日志格式为 JSON，根据是否使用颜色选择相应的格式化器
            formatter = (
                ColoredJsonFormatter() if self.colorize_console else JsonFormatter()
            )
        else:
            # 对于其他格式，根据是否使用颜色选择相应的格式化器
            formatter = (
                ColoredFormatter(self.FORMAT_MAP[self.log_format])
                if self.colorize_console
                else logging.Formatter(self.FORMAT_MAP[self.log_format])
            )
        # 设置处理程序的格式化器
        console_handler.setFormatter(formatter)
        # 将处理程序添加到日志记录器
        self.logger.addHandler(console_handler)

    def add_file_handler(self, log_file):
        """
        添加文件处理程序，用于将日志写入文件。
        根据日志格式选择合适的格式化器。

        :param log_file: 日志文件的路径
        """
        # 创建一个滚动文件处理程序，设置最大文件大小和备份数量
        file_handler = RotatingFileHandler(
            log_file, maxBytes=10 * 1024 * 1024, backupCount=5
        )
        # 设置处理程序的日志级别
        file_handler.setLevel(self.logger.level)
        if self.log_format == "json":
            # 如果日志格式为 JSON，使用 JsonFormatter 格式化器
            file_handler.setFormatter(JsonFormatter())
        else:
            # 对于其他格式，使用标准的日志格式化器
            file_handler.setFormatter(
                logging.Formatter(self.FORMAT_MAP[self.log_format])
            )
        # 将处理程序添加到日志记录器
        self.logger.addHandler(file_handler)

    def _get_caller_info(self):
        """
        获取调用日志方法的函数名和行号。

        :return: 调用函数的名称和行号
        """
        # 获取当前帧的上一帧的上一帧
        frame = inspect.currentframe().f_back.f_back
        # 返回调用函数的名称和行号
        return frame.f_code.co_name, frame.f_lineno

    def success(self, message):
        """
        记录成功级别的日志信息。

        :param message: 日志消息
        """
        # 获取调用函数的名称和行号
        caller_function, caller_line = self._get_caller_info()
        # 记录成功级别的日志
        self.logger.log(
            21,
            message,
            extra={"caller_function": caller_function, "caller_line": caller_line},
        )

    def debug(self, message):
        """
        记录调试级别的日志信息。

        :param message: 日志消息
        """
        # 获取调用函数的名称和行号
        caller_function, caller_line = self._get_caller_info()
        # 记录调试级别的日志
        self.logger.debug(
            message,
            extra={"caller_function": caller_function, "caller_line": caller_line},
        )

    def info(self, message):
        """
        记录信息级别的日志信息。

        :param message: 日志消息
        """
        # 获取调用函数的名称和行号
        caller_function, caller_line = self._get_caller_info()
        # 记录信息级别的日志
        self.logger.info(
            message,
            extra={"caller_function": caller_function, "caller_line": caller_line},
        )

    def warning(self, message):
        """
        记录警告级别的日志信息。

        :param message: 日志消息
        """
        # 获取调用函数的名称和行号
        caller_function, caller_line = self._get_caller_info()
        # 记录警告级别的日志
        self.logger.warning(
            message,
            extra={"caller_function": caller_function, "caller_line": caller_line},
        )

    def error(self, message):
        """
        记录错误级别的日志信息。

        :param message: 日志消息
        """
        # 获取调用函数的名称和行号
        caller_function, caller_line = self._get_caller_info()
        # 记录错误级别的日志
        self.logger.error(
            message,
            extra={"caller_function": caller_function, "caller_line": caller_line},
        )

    def set_level(self, level):
        """
        设置日志记录器的级别，并更新所有处理程序的级别。

        :param level: 新的日志级别
        """
        # 设置日志记录器的级别
        self.logger.setLevel(self.LEVEL_MAP.get(level.lower(), logging.DEBUG))
        # 遍历所有处理程序，更新其日志级别
        for handler in self.logger.handlers:
            handler.setLevel(self.logger.level)


class JsonFormatter(logging.Formatter):
    """
    自定义的 JSON 格式化器，用于将日志记录转换为 JSON 格式。
    """

    def format(self, record):
        """
        重写 format 方法，调用 _format_json 方法将日志记录转换为 JSON 字符串。

        :param record: 日志记录对象
        :return: 格式化后的 JSON 字符串
        """
        return self._format_json(record)

    def _format_json(self, record):
        """
        将日志记录转换为 JSON 字符串。

        :param record: 日志记录对象
        :return: 格式化后的 JSON 字符串
        """
        return (
            f'{{"time": "{self.formatTime(record, "%Y-%m-%d %H:%M:%S")}", '
            f'"level": "{record.levelname}", '
            f'"file": "{record.filename}", '
            f'"caller_function": "{getattr(record, "caller_function", "N/A")}", '
            f'"line": {getattr(record, "caller_line", 0)}, '
            f'"message": "{record.getMessage()}"}}'
        )


class ColoredJsonFormatter(logging.Formatter):
    """
    自定义的彩色 JSON 格式化器，用于将日志记录转换为彩色的 JSON 格式。
    """

    def __init__(self):
        """
        初始化彩色 JSON 格式化器，设置颜色代码和重置代码。
        """
        super().__init__()
        # 获取 logx 类中定义的颜色代码
        self.color_codes = logx.COLOR_CODES
        # 获取重置颜色的代码
        self.reset_code = self.color_codes["RESET"]

    def format(self, record):
        """
        重写 format 方法，根据日志级别添加颜色代码，并调用 _format_json 方法将日志记录转换为 JSON 字符串。

        :param record: 日志记录对象
        :return: 格式化后的彩色 JSON 字符串
        """
        # 根据日志级别获取相应的颜色代码
        level_color = self.color_codes.get(record.levelname, self.color_codes["RESET"])
        # 调用 _format_json 方法将日志记录转换为 JSON 字符串
        formatted_json = self._format_json(record)
        # 返回添加了颜色代码的 JSON 字符串
        return f"{level_color}{formatted_json}{self.reset_code}"


class ColoredFormatter(logging.Formatter):
    """
    自定义的彩色格式化器，用于将日志记录转换为彩色的文本格式。
    """

    def __init__(self, fmt):
        """
        初始化彩色格式化器，设置格式字符串、颜色代码和重置代码。

        :param fmt: 日志格式字符串
        """
        super().__init__(fmt)
        # 获取 logx 类中定义的颜色代码
        self.color_codes = logx.COLOR_CODES
        # 获取重置颜色的代码
        self.reset_code = self.color_codes["RESET"]

    def format(self, record):
        """
        重写 format 方法，根据日志级别添加颜色代码，并调用父类的 format 方法进行格式化。

        :param record: 日志记录对象
        :return: 格式化后的彩色日志记录
        """
        # 根据日志级别获取相应的颜色代码
        level_color = self.color_codes.get(record.levelname, self.color_codes["RESET"])
        # 调用父类的 format 方法进行格式化
        formatted_record = super().format(record)
        # 返回添加了颜色代码的日志记录
        return f"{level_color}{formatted_record}{self.reset_code}"


# 初始化全局日志实例
global_logger = logx(
    name="global_logger",
    level="info",
    log_file="global.log",
    log_format="detailed",
    colorize_console=True,
)


if __name__ == "__main__":
    # 打印提示信息
    print("请使用 logx.py 作为模块导入，而不是直接运行。")
